home *** CD-ROM | disk | FTP | other *** search
/ Aminet 50 / Aminet 50 (2002)(GTI - Schatztruhe)[!][Aug 2002].iso / Aminet / disk / bakup / DSrestore.lha / DSrestore / DSrestore.c < prev    next >
C/C++ Source or Header  |  2002-06-13  |  19KB  |  528 lines

  1. /* DSrestore.c */
  2. /* Copyright 2001-2002 Oliver B. Warzecha <obw@amarok.ping.de> */
  3. /*
  4.     This file is part of DSrestore.
  5.  
  6.     DSrestore is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     DSrestore is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with DSrestore; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19. */
  20.  
  21.  
  22. #include <proto/exec.h>
  23. #include <proto/dos.h>
  24.  
  25. #include <exec/memory.h>
  26. #include <dos/rdargs.h>
  27.  
  28. #include <ds_stream.h>
  29.  
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <strings.h>
  33.  
  34. #include <dos.h>
  35.  
  36. #include "DSrestore.h"
  37. #include "DSrestore_rev.h"
  38.  
  39. static const char* verID = VERSTAG;
  40. /* defaults for development */
  41.  
  42. const char *fbtarget="Sys:";
  43.  
  44. static long debug;
  45.  
  46. struct RDArgs *rdArgs;
  47.  
  48. char *target,*backup;
  49. char *buff;
  50. int err;
  51. void *mypool;
  52.  
  53.  
  54. BPTR infile,tmplock,StdErr,logFile;
  55.  
  56. int main(int argc, char ** argv)
  57. {
  58.     int rc=0;
  59.     logFile = 0;
  60.  
  61.     StdErr=Open("CONSOLE:", MODE_OLDFILE);
  62.     /* here we parse the commandline */
  63.     mypool = CreatePool(MEMF_CLEAR,4096,STBLOCKSIZE);
  64.     if (mypool == NULL) {
  65.         FPrintf(StdErr, "unable to create memory pool");
  66.         Close(StdErr);
  67.         return 20;
  68.     }
  69.     read_params();
  70.     /*
  71.      * now we know the name of the archive and the directory to 
  72.      * place the restored data in.
  73.      */
  74.     if (!parse_stream(backup)) {
  75.         PrintFault(IoErr(), "DSrestore problem: ");
  76.         rc = 10;
  77.     }
  78.     /*
  79.      * we could do more here.
  80.     */
  81.     if(buff)
  82.         FreeVec(buff);
  83.     FreeArgs(rdArgs);
  84.     FreeVec(target);
  85.     FreeVec(backup);
  86.     DeletePool(mypool);
  87.     Close(infile);
  88.     Close(StdErr);
  89.     return rc;
  90. }
  91.  
  92. void read_params(void)
  93. {
  94.  
  95.     char *argStr = ARGSTRING;
  96.     LONG argArray[4];
  97.     LONG errc;
  98.  
  99.     /* set defaults */
  100. /* target = current directory */
  101.     buff = AllocVec(256, MEMF_CLEAR);
  102.     if (!(tmplock = Lock("PROGDIR:", ACCESS_READ)))
  103.         FPrintf(StdErr,"lock on current dir failed\n");
  104.     if (!NameFromLock(tmplock, buff, 255)) {
  105.         if ((errc = IoErr()) == ERROR_LINE_TOO_LONG)
  106.             FPrintf(StdErr, "Current directory name too long, using SYS: (this is probably not what you want)\n");
  107.         else {
  108.             PrintFault(errc, "Error on current dir");
  109.         }
  110.         FreeVec(buff);
  111.         buff = NULL;
  112.         argArray[1] = (LONG)fbtarget;
  113.     } else
  114.         argArray[1] = (LONG)buff;
  115.     UnLock(tmplock);
  116.     argArray[2] = 0;
  117.     if (rdArgs = ReadArgs(argStr, argArray, NULL)) {
  118.         debug = argArray[2];
  119.         target = AllocVec(strlen((char *)argArray[1])+1UL, MEMF_CLEAR);
  120.         strcpy(target, (char *)argArray[1]);
  121.         backup = AllocVec(strlen((char *)argArray[0])+1UL, MEMF_CLEAR);
  122.         strcpy(backup, (char *)argArray[0]);
  123.     } else {
  124.         FPrintf(StdErr,"required argument missing\n");
  125.         if (buff)
  126.             FreeVec(buff);
  127.         FreeArgs(rdArgs);
  128.         rdArgs = NULL;
  129.         Close(StdErr);
  130.         DeletePool(mypool);
  131.         SetIoErr(ERROR_REQUIRED_ARG_MISSING);
  132.         exit(20);
  133.     }
  134.     if (argArray[3]) {
  135.         logFile = Open((STRPTR)argArray[3], MODE_NEWFILE);
  136.         if (!logFile) {
  137.             FPrintf(StdErr, "couldn't open file %s for logging, continuing\n", argArray[3]);
  138.         }
  139.     }
  140.     FreeArgs(rdArgs);
  141.     rdArgs = NULL;
  142.     return;
  143. }
  144. /*
  145.  * readnext
  146.  * read a block from the stream in the supplied buffer
  147.  */
  148.  
  149.  
  150. void readnext(BPTR infile, void* buffer)
  151. {
  152.     LONG size;
  153.     
  154.     size = Read(infile, buffer, STBLOCKSIZE);
  155.     /*
  156.      * sophisticated error handling. Is the stream padded?
  157.      */
  158.     if (size != STBLOCKSIZE)
  159.         FPrintf(StdErr,"Short read at file position %ld", Seek(infile, 0, OFFSET_CURRENT));
  160.     return;
  161. }
  162.  
  163. /*
  164.  * API for ID->Array conversion, put in another file?
  165.  */
  166.  
  167. static long *IDarr=NULL;
  168. static long maxid=0;
  169.  
  170. /* constructor */
  171.  
  172. BOOL ID__ID(long nid)
  173. {
  174.     if (!(IDarr =(long *)AllocPooled(mypool, (ULONG)((size_t)nid * sizeof (long)))))
  175.         return FALSE;
  176.     maxid = nid;
  177.     return TRUE;
  178. }
  179.  
  180. /* destructor */
  181.  
  182. BOOL IDn_ID(void)
  183. {
  184.     if (IDarr == NULL)
  185.         return FALSE;
  186.     /* free the whole array */
  187.     maxid = 0;
  188.     FreePooled(mypool, IDarr, (ULONG)(maxid * sizeof(long)));
  189.     return TRUE;
  190. }
  191.  
  192. long indexID(long id)
  193. {
  194.     long sc=0;
  195.  
  196.     while (IDarr[sc] != id && sc != maxid)
  197.         sc++;
  198.     if (maxid == sc) {
  199.         FPrintf(StdErr,"object with ID %ld not found.\n", id);
  200.         return -1;              /* sign for error */
  201.     }
  202.     if (debug)
  203.         Printf("searching index for ID %ld, found at %ld\n",id,sc);
  204.     return sc;
  205. }
  206.  
  207. long getID(long index)
  208. {
  209.     return IDarr[index];
  210. }
  211.  
  212. BOOL setID(long id, long index)
  213. {
  214.     if (index >= maxid)
  215.         return FALSE;
  216.     if (debug)
  217.         Printf("setting index %ld to ID %ld\n",index, id);
  218.     IDarr[index] = id;
  219.     return TRUE;
  220. }
  221.  
  222. /*
  223.  * we have the name of the stream to parse
  224.  * Make it so!
  225.  */
  226.  
  227. BOOL parse_stream(const char* name)
  228. {
  229.     void *rootbuffer;
  230.     void *block=NULL;
  231.     STRoot* BRoot;
  232.     STHeader* BHeader;
  233.     char **Pathlist;
  234.     STBaseUnit *counters;
  235.     short fixbug = 0;
  236.     int NPaths;
  237.         
  238.     /* open the file, non existing error handling again */
  239.     if (!(infile = Open((STRPTR)name, MODE_OLDFILE))) {
  240.         FPrintf(StdErr,"Unable to open archive file %s\n",(LONG)name);
  241.         return FALSE;
  242.     }
  243.     /* there _should_ be enough memory, doesn't it? */
  244.     rootbuffer = AllocPooled(mypool, STBLOCKSIZE);
  245.     counters = AllocPooled(mypool, sizeof(STBaseUnit));
  246.     /*
  247.      * The file is open. Read the first block and check, if it is really
  248.      * a disksalv stream and not a jpg or anything.
  249.      * so we do a consistency check!
  250.      */
  251.     readnext(infile, rootbuffer);
  252.     BRoot = (STRoot*)rootbuffer;
  253.     /*
  254.      * this is the number of objects! We need to store this in a safe area.
  255.      * best if we leave the rootblock alone for the duration of the process.
  256.      * we also need a counter to count the objects for ourselves.
  257.      */
  258.     if (debug)
  259.         Printf("%ld Objekte im Archiv\n",BRoot->barcount);
  260.     if ((BRoot->head.type != ID_ROOT) || (BRoot->head.id != 0)) {
  261.         FPrintf(StdErr,"no rootblock or unknown archive format!\nexiting...\n");
  262.         SetIoErr(ERROR_OBJECT_WRONG_TYPE);
  263.         return FALSE;
  264.     }
  265.     /* now the business starts */
  266.     /*
  267.      * we loop through the whole archive until the last block is read.
  268.      * last block is recognized by ID_ENDA. Every object is identified by
  269.      * the unique(!) ID number. So we have to store it somehow to handle links,
  270.      * subdirs, etc. we need the ID and the full path. sounds like a stringarray to
  271.      * me. and we know the size, too! ID does not start with zero, seems to
  272.      * be the disk key. :-( storage Methods: utility namespace objects?
  273.      * No, just a second array. I have to provide a small API, see above for
  274.      * #?ID()-Functions.
  275.      */
  276.     if (!ID__ID(BRoot->barcount))
  277.         FPrintf(StdErr,"failed to allocate ID array\n");
  278.     /*
  279.      * Oh, hazy, what have you done! Objects in the root directory have a parent
  280.      * ID containing the block number of the root block! But the object containing
  281.      * this ID is of course _not_ in the archive stream. So I have to assume that the first
  282.      * object in the archive is in the root dir. sigh!
  283.      */
  284.     NPaths = (size_t)BRoot->barcount * sizeof(char *);
  285.     if ((Pathlist = (char**)AllocPooled(mypool, (ULONG)NPaths)) == NULL)
  286.         FPrintf(StdErr,"Allocation for object array failed\n");
  287.     if (!(tmplock = Lock((STRPTR)target, ACCESS_READ))) {
  288.         FPrintf(StdErr,"Unable to find target dir %s\n",(LONG)target);
  289.         return FALSE;
  290.     }
  291.     Pathlist[0] = (char *)AllocPooled(mypool, 255 * sizeof(char));
  292.     /* 255 should be the maximum length of a BSTR (volumename) */
  293.     if (!(NameFromLock(tmplock, (STRPTR)Pathlist[0], 254))) {
  294.         FPrintf(StdErr,"could not get path for target dir %s, going downhill...\n", (LONG)target);
  295.         return FALSE;
  296.     }
  297.     UnLock(tmplock);
  298.  
  299.     /* we give the root dir an ID of 0 first */
  300.     if (!setID(0,0)) {
  301.         return FALSE; /* the allocation above must have failed */
  302.     }
  303.     counters->objectcount = 0;
  304.     do {
  305.         if (block)
  306.             FreePooled(mypool, block, STBLOCKSIZE);
  307.         if (CheckSignal(SIGBREAKF_CTRL_C)) {
  308.             SetIoErr(ERROR_BREAK);
  309.             return FALSE;
  310.         }
  311.         block=AllocPooled(mypool, STBLOCKSIZE);
  312.         readnext(infile, block);
  313.         BHeader = (STHeader *)block;
  314.         if (BHeader->type == ID_DATA || BHeader->type == ID_ENDA)
  315.             continue;
  316.     /* store ID */
  317.     /*
  318.      * yeeha! broken DiskSalv (?)! the current ID can be 0. there is it. :-P
  319.      */
  320.         if (fixbug == 1) {
  321.             if (BHeader->type == ID_UDIR && BHeader->parent == counters->dwalloc) {
  322.             /* last one was an directory, both have the same parent -> last dir was empty.
  323.              * so the ID of the last dir is unimportant, set it to 0. */
  324.                 setID(0,counters->objectcount);
  325.             } else {
  326.                 setID(BHeader->parent,counters->objectcount);
  327.                 FPrintf(StdErr,"Bugfix: set ID of object no. %ld to %ld\n",counters->objectcount,BHeader->parent);
  328.             }
  329.             fixbug = 0;
  330.         }
  331.         if (BHeader->id == 0){
  332.             fixbug = 1;
  333.             counters->dwalloc = BHeader->parent;
  334.         }
  335.         counters->objectcount++;
  336.         if (!setID(BHeader->id,counters->objectcount))
  337.             FPrintf(StdErr,"ID storage for %ld failed", (LONG)BHeader->id);
  338. /*
  339.  * TODO:    check, if ID already allocated
  340.  *          more consistency checks at all
  341.  *          clean up code
  342.  */
  343.  
  344.     /*
  345.      * special case for 1st object.
  346.      * fetch parent ID and store as ID for root
  347.      */
  348.         if (1 == counters->objectcount) {
  349.             setID(BHeader->parent,0);
  350.         }
  351.     /* the parent should already have been handled, get lock for parent name */
  352.         if (debug)
  353.             Printf("parent ID: %ld", BHeader->parent);
  354.  
  355.         if (Pathlist[indexID(BHeader->parent)] == NULL) {
  356.             long i;
  357.             PutStr("parent not yet initialised, exiting\n");
  358.             for (i=0; Pathlist[i] != NULL; i++)
  359.                 FPrintf(StdErr,"Pathlist[%ld]=%s\n",i,Pathlist[i]);
  360.             SetIoErr(ERROR_DIR_NOT_FOUND);
  361.             return FALSE;
  362.         }
  363.         if (debug)
  364.             Printf(
  365.                 "current object: %s, parent: %s\n",
  366.                 (LONG)((STDir*)BHeader)->body.filename,
  367.                 Pathlist[indexID(BHeader->parent)] );
  368.         counters->dwsize = strlen(Pathlist[indexID(BHeader->parent)]);
  369.         if (debug)
  370.             Printf("first path component length: %ld, current filename %s\n",
  371.                 counters->dwsize,
  372.                 ((STDir*)BHeader)->body.filename );
  373.     /* space for filenames + \0 + separator */
  374.         counters->dwsize += (strlen(((STDir*)BHeader)->body.filename)+2);
  375.     /* counters->out as temp pointer from STBaseUnit */
  376.         if (debug)
  377.             Printf("Allocating %ld bytes\n", counters->dwsize);
  378.         counters->out = AllocPooled(mypool, counters->dwsize);
  379.         Pathlist[counters->objectcount] = counters->out;
  380.         strcpy(counters->out, Pathlist[indexID(BHeader->parent)]);
  381.     /* counters->out is the path of the object */
  382.         if (!(AddPart(counters->out,((STDir*)BHeader)->body.filename, counters->dwsize)))
  383.             FPrintf(StdErr,"Oops! AddPart() failed.\n");
  384.         if (debug)
  385.             Printf("complete path: %s\n",(LONG)counters->out);
  386.     /* we don't need the old lock any more */
  387.         tmplock = 0;
  388.     /* state machine for block type */
  389.         switch (BHeader->type) {
  390.             case ID_UDIR:       /* directory */
  391.     /* create directory, set all attribs. */
  392.                 counters->dircount++;
  393.                 if (logFile)
  394.                     FPrintf(logFile, "UDIR %s\n", (LONG)counters->out);
  395.                 if (!(tmplock = CreateDir(counters->out))) {
  396.                     FPrintf(StdErr,"CreateDir(\"%s\") failed;", (LONG)counters->out);
  397.                     if (ERROR_OBJECT_EXISTS != IoErr()) {
  398.                         FPrintf(StdErr, "\n");
  399.                         return FALSE;
  400.                     }
  401.                     FPrintf(StdErr," object already exists, ignoring...\n");
  402.                 }
  403.           /* set protection, date and filenote */
  404.                 SetComment(counters->out, ((STDir *)BHeader)->body.filenote);
  405.                 SetProtection(counters->out, ((STDir *)BHeader)->body.protect);
  406.                 SetFileDate(counters->out, &((STDir *)BHeader)->body.date);
  407.                 break;
  408.             case ID_FILE:       /* file */
  409.     /*
  410.      * make a lock on parent object, which should be a directory. open file for
  411.      * writing, push all data into it, close it. Then set protection, filenote and date.
  412.      * That's all. ;)
  413.      */
  414.                 counters->filecount++;
  415.                 if (debug)
  416.                     Printf("opening file %s\n", (LONG)counters->out);
  417.                 if (logFile)
  418.                     FPrintf(logFile, "FILE %s\n", (LONG)counters->out);
  419.                 if (tmplock = Lock(counters->out, ACCESS_READ)) {
  420.                     UnLock(tmplock);
  421.                     counters->out = strcat(counters->out, "_1");
  422.                 }
  423.                 tmplock = Open(counters->out, MODE_NEWFILE);
  424.                 if (tmplock)
  425.                     writeFile(infile, tmplock, (ULONG)BHeader->size);
  426.                 else {
  427.                     PrintFault(IoErr(),"Error ");
  428.                     Close(tmplock);
  429.                     tmplock = 0;
  430.                     return FALSE;
  431.                 }
  432.                 Close(tmplock);
  433.                 tmplock = 0;
  434.                 if (debug)
  435.                     Printf("Finished writing\n");
  436.                 SetComment(counters->out, ((STDir *)BHeader)->body.filenote);
  437.                 SetProtection(counters->out, ((STDir *)BHeader)->body.protect);
  438.                 SetFileDate(counters->out, &((STDir *)BHeader)->body.date);
  439.                 break;
  440.             case ID_FLNK:       /* hardlink to file */
  441.             case ID_DLNK:       /* hardlink to dir */
  442.                 counters->linkcount++;
  443.                 if (logFile)
  444.                     FPrintf(logFile, "LINK %s\n", (LONG)counters->out);
  445.                 if (!(tmplock = Lock(Pathlist[indexID(((STHLink*)BHeader)->link)],ACCESS_READ)))
  446.                     FPrintf(StdErr,"Could not lock file %s to link to\n",
  447.                             (LONG)Pathlist[indexID(((STHLink*)BHeader)->link)]);
  448.                 if (tmplock)
  449.                     if (!MakeLink(counters->out, (LONG)tmplock, FALSE))
  450.                         FPrintf(StdErr,"MakeLink() failed for hardlink %s\n",
  451.                             (LONG)counters->out);
  452.                 SetComment(counters->out, ((STDir *)BHeader)->body.filenote);
  453.                 SetProtection(counters->out, ((STDir *)BHeader)->body.protect);
  454.                 SetFileDate(counters->out, &((STDir *)BHeader)->body.date);
  455.                 break;
  456.             case ID_SLNK:       /* softlink */
  457.                 counters->linkcount++;
  458.                 if (logFile)
  459.                     FPrintf(logFile, "SLNK %s\n", (LONG)counters->out);
  460.                 if (!MakeLink(counters->out, (LONG)((STSLink*)BHeader)->link, TRUE))
  461.                     FPrintf(StdErr,"MakeLink() failed for softlink %s\n",
  462.                         (LONG)counters->out);
  463.                 tmplock = 0;
  464.                 break;
  465.             case ID_ERRS:       /* huh? */
  466.                 counters->errcount++;
  467.                 FPrintf(StdErr,"Found error block. I don't know how to handle it.\n");
  468.                 break;
  469.             case SET_DEL:       /* error, may be deleted? */
  470.                 FPrintf(StdErr,"The file %s was faulty, according to DiskSalv.\n",
  471.                         (LONG)Pathlist[counters->objectcount - 1]);
  472.                 break;
  473.             default:
  474.                 FPrintf(StdErr,"Unknown block encountered. Archive format ok?\n");
  475.                 break;
  476.         }
  477.         UnLock(tmplock);
  478.     } while (BHeader->type !=ID_ENDA);
  479.     Printf("%ld files, %ld directories and %ld links restored.\n%ld errors encountered.\n",
  480.             counters->filecount,
  481.             counters->dircount,
  482.             counters->linkcount,
  483.             counters->errcount);
  484.     return TRUE;
  485. }
  486.      
  487. BOOL writeFile(BPTR source, BPTR target, LONG size)
  488. {
  489.     char *tbuff, *sbuff;
  490.     ULONG bcount, i;
  491.  
  492.     /* optimize... read all and/or write all in one go... done */
  493.     if (size == 0)
  494.         return TRUE;
  495.     /*
  496.      * allocate all necessary memory, 2 buffers for source and target
  497.      */
  498.     bcount = size / STDATASIZE + (size % STDATASIZE ? 1 : 0);
  499.     if (debug)
  500.         Printf("%ld blocks of memory, %ld byte together, available %ld.\n", bcount, size, AvailMem(MEMF_FAST|MEMF_LARGEST));
  501.     if (tbuff = AllocVec(bcount * STDATASIZE, 0L)) {
  502.         if (sbuff = AllocVec(bcount * STBLOCKSIZE, 0L)) {
  503.             if (bcount * STBLOCKSIZE != Read(source, sbuff, bcount * STBLOCKSIZE))
  504.                 PutStr("Short read\n");
  505.             for (i = 0; i < bcount; i++)
  506.                 if (((STHeader *)(sbuff + i * STBLOCKSIZE))->type != ID_DATA) { /* find wrong counts */
  507.                     if (debug)
  508.                         Printf("Seek %ld bytes, current pos %ld\n", -((LONG)(bcount - i))*STBLOCKSIZE, Seek(source, 0, OFFSET_CURRENT));
  509.                     Seek(source, -(LONG)((bcount - i)*STBLOCKSIZE), OFFSET_CURRENT);  /* set to start of non-data */
  510.                     break;
  511.                 }
  512.             for (i = 0; i < bcount; i++) {      /* copy all data to the compact databuffer */
  513.                 CopyMem(sbuff + STBLOCKSIZE*i + sizeof(STHeader), tbuff + STDATASIZE*i, STDATASIZE);
  514.             }
  515.             if (size != Write(target, tbuff, size)) {
  516.                 PutStr("Short write\n"); /* TODO: there should be more error handling */
  517.             }
  518.             FreeVec(sbuff);
  519.         } else {
  520.             FPrintf(StdErr,"source buffer of %ld failed\n", bcount*STBLOCKSIZE);
  521.         }
  522.         FreeVec(tbuff);
  523.     } else {
  524.         FPrintf(StdErr,"target buffer of %ld failed\n", size);
  525.     }
  526.     return TRUE;
  527. }
  528.